package ae.pcfc.etks.online.itworx.ehs.web.facade.impl;

import java.net.ConnectException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;

import ae.pcfc.etks.online.common.ConstantsCommon.WFProcessType;
import ae.pcfc.etks.online.common.delegate.AppMetaDataDelegate;
import ae.pcfc.etks.online.common.vo.AppMetaDataVO;
import ae.pcfc.etks.online.fnp.exception.FNPException;
import ae.pcfc.etks.online.fnp.service.FNPService;
import ae.pcfc.etks.online.itworx.ehs.web.common.dozer.convertors.OFCRequestConvertor;
import ae.pcfc.etks.online.itworx.ehs.web.constants.GlobalConstants;
import ae.pcfc.etks.online.itworx.ehs.web.entity.OFCRequest;
import ae.pcfc.etks.online.itworx.ehs.web.entity.OFCRequestHistory;
import ae.pcfc.etks.online.itworx.ehs.web.entity.Request;
import ae.pcfc.etks.online.itworx.ehs.web.entity.Service;
import ae.pcfc.etks.online.itworx.ehs.web.enums.RequestAction;
import ae.pcfc.etks.online.itworx.ehs.web.enums.RequestStatus;
import ae.pcfc.etks.online.itworx.ehs.web.enums.RequestType;
import ae.pcfc.etks.online.itworx.ehs.web.enums.UserType;
import ae.pcfc.etks.online.itworx.ehs.web.enums.WorkflowDataFields;
import ae.pcfc.etks.online.itworx.ehs.web.exception.CommunicationException;
import ae.pcfc.etks.online.itworx.ehs.web.exception.FNPServiceExpcetion;
import ae.pcfc.etks.online.itworx.ehs.web.exception.IMSException;
import ae.pcfc.etks.online.itworx.ehs.web.exception.InvalidInputException;
import ae.pcfc.etks.online.itworx.ehs.web.exception.WorkFlowException;
import ae.pcfc.etks.online.itworx.ehs.web.facade.WorkflowFacade;
import ae.pcfc.etks.online.itworx.ehs.web.manager.IMSManager;
import ae.pcfc.etks.online.itworx.ehs.web.manager.WorkflowManager;
import ae.pcfc.etks.online.itworx.ehs.web.model.RequestSearchCriteria;
import ae.pcfc.etks.online.itworx.ehs.web.service.WorkflowService;
import ae.pcfc.etks.online.itworx.ehs.web.util.UtilCommon;

//TODO need to be revised, why do we have this and we already have the WS impl module!!!
public class WorkflowFacadeImpl extends BaseFacade implements WorkflowFacade {

	private WorkflowManager workflowManager;
	private WorkflowService workflowService;
	private FNPService fnpService;
	private IMSManager imsManager;
	
	private Logger log = LoggerFactory.getLogger(WorkflowFacadeImpl.class);
	
	private AppMetaDataDelegate trackAppMetaDataDelegate;
	
	@Resource(name = "trackAppMetaDataDelegate")
	public void setTrackAppMetaDataDelegate(AppMetaDataDelegate trackAppMetaDataDelegate){
		this.trackAppMetaDataDelegate = trackAppMetaDataDelegate;
	}
	
	public IMSManager getImsManager() {
		return imsManager;
	}

	public void setImsManager(IMSManager imsManager) {
		this.imsManager = imsManager;
	}

	public FNPService getFnpService() {
		return fnpService;
	}

	public void setFnpService(FNPService fnpService) {
		this.fnpService = fnpService;
	}

	public WorkflowService getWorkflowService() {
		return workflowService;
	}

	public void setWorkflowService(WorkflowService workflowService) {
		this.workflowService = workflowService;
	}

	public WorkflowManager getWorkflowManager() {
		return workflowManager;
	}

	public void setWorkflowManager(WorkflowManager workflowManager) {
		this.workflowManager = workflowManager;
	}

	@Transactional(readOnly = true)
	public List<OFCRequest> getAllRequestsByStatus(
			List<RequestStatus> requestsStatus) {
		List<OFCRequest> requests = workflowManager.getAllRequestsByStatus(requestsStatus);
		return requests; 
		
	}

	@Transactional(readOnly = true)
	public OFCRequest getRequestDetails(Long requestId, String username, boolean isOfficer)
			throws InvalidInputException, WorkFlowException {
		
		List<String> taskUsers = null;
		String[] delegatingUsers = null;
		if(isOfficer){
			delegatingUsers = workflowService.getDelegatedUsers(username);
			taskUsers = new ArrayList<String>();
			for(String userStr: delegatingUsers){
				taskUsers.add(userStr.toLowerCase());
			}
		}
		// 1- get the ofcRequest from EHS DB
		OFCRequest ofcRequest = workflowManager.getRequestByIdAndUsername(requestId, username, taskUsers, isOfficer);

		if (null == ofcRequest) {
			InvalidInputException invalidInputExp=new InvalidInputException();
			invalidInputExp.setErrorCode("error.ofcRequestModel.accessDenaied");
			throw invalidInputExp;
		}
		ofcRequest.getFnpList();
		ofcRequest.getComments();
		ofcRequest.getOfcHistory();
		ofcRequest.getDocuments();
		return ofcRequest;
	}
	
	private List<String> getDelegateUsers(String username) throws WorkFlowException{
		List<String> taskUsers = null;
		String[] delegatingUsers = null;
			delegatingUsers = workflowService.getDelegatedUsers(username);
			taskUsers = new ArrayList<String>();
			for(String userStr: delegatingUsers){
				taskUsers.add(userStr.toLowerCase());
			}
		return 	taskUsers;
	}

	@Transactional(readOnly = true)
	public List<OFCRequestHistory> getRequestHistory(Long requestId) {
		return workflowManager.getRequestHistory(requestId);
	}

	@Transactional
	public void createOFCRequest(OFCRequest ofcRequest)
			throws InvalidInputException, CommunicationException {
		//int processId = -1;
		if (ofcRequest != null) {
			OFCRequestConvertor ofcRequestConvertor = new OFCRequestConvertor();
			Map<String, String> workflowMap = new HashMap<String, String>();
			workflowMap = ofcRequestConvertor
					.convertTo(ofcRequest, workflowMap);
			/*processId = workflowService.createProcess(
					WorkflowDataFields.ProcessName.getPropertyName(),
					workflowMap);
			if (processId <= -1) {
				System.out.println("Roll Baaaaaaaaaaaaaaaaack");
				throw new WorkFlowException("Workflow Exception");
			} else {
				ofcRequest.setProcessId(String.valueOf(processId));*/
				OFCRequestHistory history = new OFCRequestHistory();
				mapper.map(ofcRequest, history);
				if (null == ofcRequest.getOfcHistory()) {
					ofcRequest.setOfcHistory(new ArrayList<OFCRequestHistory>());
				}
				ofcRequest.getOfcHistory().add(history);
				
				Date formattedProcessDate = UtilCommon.getFormattedDate(ofcRequest.getProcessingDate());
				ofcRequest.setProcessingDate(formattedProcessDate);
				workflowManager.createOFCRequest(ofcRequest);
				trackRequestChange(ofcRequest,false);
			//}
		} else {
			throw new InvalidInputException("OFCRequest can't be null");
		}
	}
	
	@Transactional
	public int createOFCProcess(OFCRequest ofcRequest)
	throws InvalidInputException, WorkFlowException {
		int processId = -1;
		if (ofcRequest != null) {
			OFCRequestConvertor ofcRequestConvertor = new OFCRequestConvertor();
			Map<String, String> workflowMap = new HashMap<String, String>();
			workflowMap = ofcRequestConvertor
					.convertTo(ofcRequest, workflowMap);
			processId = workflowService.createProcess(
					WorkflowDataFields.ProcessName.getPropertyName(),
					workflowMap);
			if (processId <= -1) {
				System.out.println("Roll Baaaaaaaaaaaaaaaaack");
				throw new WorkFlowException("Workflow Exception");
			}
		}
		return processId;
	}
	@Transactional
	public void updateOFCRequest(OFCRequest ofcRequest) {
//		SimpleDateFormat smp = new SimpleDateFormat("dd/MM/yyyy");
//		String formatedDate = smp.format(ofcRequest.getProcessingDate());
//		Date formattedProcessDate = smp.parse(formatedDate);
//		
		Date formattedProcessDate = UtilCommon.getFormattedDate(ofcRequest.getProcessingDate());
		ofcRequest.setProcessingDate(formattedProcessDate);
		workflowManager.updateOFCRequest(ofcRequest);
		trackRequestChange(ofcRequest,true);
	}

	public boolean isOfficer(String username) throws CommunicationException {
		return workflowService.isOfficer(username);
	}

	public void doRequestAction(Long requestId, String doAction,
			String userName, RequestAction actinoType, boolean isOfficer)
			throws WorkFlowException, InvalidInputException {

		Map<String, String> workflowMap = new HashMap<String, String>();
		workflowMap.put("status", RequestAction.getAssociatedStatus(actinoType)
				.getStatusName());
		if (workflowService.doAction(doAction, userName,
				actinoType.getActionName(), workflowMap, isOfficer)) {
			
			//TODO why is that?
			/*OFCRequest ofcRequest = workflowManager
					.getRequestDetails(requestId);
			ofcRequest.setStatus(RequestAction.getAssociatedStatus(actinoType));
			workflowManager.updateOFCRequest(ofcRequest);*/
		} else {
			// TODO:throw exception ??
		}
	}

	@Override
	@Transactional
	public List<Request> getAllRequests(String userName, boolean isOfficer,
			RequestSearchCriteria criteria) throws InvalidInputException,
			CommunicationException, RuntimeException {
		List<Request> requsts = workflowManager.getAllRequests(userName,
				isOfficer, criteria);
		return requsts;
	}

	@Override
	@Transactional
	public List<Request> getActiveTasks(RequestSearchCriteria criteria,
			String processName, String userName, boolean isOffecier,
			boolean isAssigned) throws WorkFlowException, InvalidInputException {

		// 1- get all users those delegating tasks to this users
		List<String> taskUsers = null;
		String[] delegatingUsers = null;
		if(isAssigned){
			delegatingUsers = workflowService.getDelegatedUsers(userName);
			taskUsers = new ArrayList<String>();
			for(String userStr: delegatingUsers){
				taskUsers.add(userStr.toLowerCase());
			}
		}
		// 2- get corresponding requests objects from our EHS DB
		List<Request> requsts = workflowManager.getAllActiveTasks(userName,
				isOffecier, criteria, taskUsers,isAssigned);
		return requsts;
	}


	@Override
	@Transactional
	public long getAllRequestsSize(String userName, boolean isOfficer,
			RequestSearchCriteria searchCriteria) throws CommunicationException,
			InvalidInputException {
		return workflowManager.getAllRequestsSize(userName, isOfficer,
				searchCriteria);
	}

	@Override
	@Transactional
	public long getAllActiveTasksSize(String processName, String userName,
			boolean isOfficer, RequestSearchCriteria searchCriteria,
			boolean isAssigned) throws CommunicationException, InvalidInputException {
		// 1- get the active tasks for the user from the workflow
		// 1- get all users those delegating tasks to this users
		String[] delegatingUsers = workflowService.getDelegatedUsers(userName);

		List<String> taskUsers = new ArrayList<String>();
		for(String userStr: delegatingUsers){
			taskUsers.add(userStr.toLowerCase());
		}
		return workflowManager.getAllActiveTasksSize(userName, isOfficer,
				searchCriteria, taskUsers,isAssigned);
	}

	@Override
	@Transactional
	public void cancelRequest(long requestId, String username, boolean isOfficer)
			throws CommunicationException, InvalidInputException,FNPServiceExpcetion,IMSException {
		if (requestId <= 0) {
			throw new InvalidInputException("Invalid Request ID");
		}
		OFCRequest ofcRequest = null;
		List<String> delegateUsers = new ArrayList<String>();
		if(isOfficer){
			delegateUsers = getDelegateUsers(username);
		}
		ofcRequest = workflowManager.getRequestByIdAndUsername(requestId, username, delegateUsers, isOfficer);

		// exclude statuses for customer
		if (null == ofcRequest
				|| (!ofcRequest.getStatus().equals(RequestStatus.Submitted)
						&& !ofcRequest.getStatus().equals(
								RequestStatus.Resubmitted)
						&& !ofcRequest.getStatus().equals(RequestStatus.Return_For_Update)
						&& !ofcRequest.getStatus()
								.equals(RequestStatus.Approved)
						&& !isOfficer)) {
			
			throw new InvalidInputException("Invalid Data");
			
		}else if (!(ofcRequest.getStatus()
				.equals(RequestStatus.Non_Compliance_Comments_Exists))
				&& !(ofcRequest.getStatus()
						.equals(RequestStatus.Comments_Replied)) && isOfficer) {
			throw new InvalidInputException("Invalid Request Status");
		}
		if(isOfficer){
			ofcRequest.setOfficerId(username);
			ofcRequest.setExecuter(UserType.Officer);
		}else{
			ofcRequest.setUserId(username);
			ofcRequest.setExecuter(UserType.User);
		}
		ofcRequest.setStatus(RequestStatus.Canceled);
		ofcRequest.setProcessingDate(new Date());
		cancelOrCloseRequest(ofcRequest, isOfficer, username, RequestAction.Cancel);
	}
	
	@Override
	@Transactional
	public void closeRequest(long requestId, String username, boolean isOfficer) throws CommunicationException, InvalidInputException,FNPServiceExpcetion,IMSException{
		
		OFCRequest ofcRequest = null;
		List<String> delegateUsers = new ArrayList<String>();
		if(isOfficer){
			delegateUsers = getDelegateUsers(username);
			ofcRequest = workflowManager.getRequestByIdAndUsername(requestId, username, delegateUsers, isOfficer);
		}else{
			throw new InvalidInputException("Access Denied");
		}
		
		
		// exclude statuses for customer
		if (null == ofcRequest
				|| (!(ofcRequest.getStatus().equals(RequestStatus.Inspection_Approved))
						&& !(ofcRequest.getStatus().equals(
								RequestStatus.Closed)))) {
			throw new InvalidInputException("Invalid Data");
		}
		ofcRequest.setOfficerId(username);
		ofcRequest.setExecuter(UserType.Officer);
		ofcRequest.setStatus(RequestStatus.Closed);
		ofcRequest.setProcessingDate(new Date());
		cancelOrCloseRequest(ofcRequest, isOfficer, username, RequestAction.Close);
	}
	
	private void cancelOrCloseRequest(OFCRequest ofcRequest, boolean isOfficer, String username, RequestAction requestAction) throws FNPServiceExpcetion,IMSException,CommunicationException {
		Map<String, String> workflowMap = new HashMap<String, String>();
		OFCRequestConvertor ofcRequestConvertor = new OFCRequestConvertor();
		workflowMap = ofcRequestConvertor.convertTo(ofcRequest, workflowMap);
		
		OFCRequestHistory history = new OFCRequestHistory();
		mapper.map(ofcRequest, history);
		if (null == ofcRequest.getOfcHistory()) {
			ofcRequest.setOfcHistory(new ArrayList<OFCRequestHistory>());
		}
		history.setOfficerComments("");
		ofcRequest.getOfcHistory().add(history);
		updateOFCRequest(ofcRequest);
		

		// TODO
		// cancel IMS and FNP if created
		if(requestAction.equals(RequestAction.Cancel)){
			String imsLogId = ofcRequest.getImsLogId();
			if (null != imsLogId && !imsLogId.isEmpty()) {
				imsManager.cancelIMPSRecord(imsLogId);
			}
		}
		
		boolean isCanceledOrClosed = workflowService.doAction(
				ofcRequest.getProcessId(), username,
				requestAction.getActionName(), workflowMap, isOfficer);
		if (!isCanceledOrClosed) {
			throw new CommunicationException("Invalid Data");
		}
		
		
		if(requestAction.equals(RequestAction.Cancel)){
			if (null != ofcRequest.getFnpList() && ofcRequest.getFnpList().size() > 0 && !isOfficer) {
				String paymentAdviceNumber = ofcRequest.getFnpList().get(0)
						.getPaymentNumber();
				if (null != paymentAdviceNumber && !paymentAdviceNumber.isEmpty()) {
					// TODO
					// cancel in fnp
					// fnpService.
					String fnpMessage = "EHS: Cancel Payment";
					String txnNo = null;
					try{
						txnNo = UtilCommon.generateFNPTransactioNum(ofcRequest.getId()+GlobalConstants.CANCEL_PAYMENT_ADVICE);
						fnpService.cancelPaymentAdvice(txnNo, paymentAdviceNumber, ofcRequest.getUserId(), fnpMessage,GlobalConstants.FNP_EHS_ONLINE_SOURCE_ID);
						
					}catch (FNPException fnpExp) {
						FNPServiceExpcetion fnpServiceExp=new FNPServiceExpcetion(fnpExp);
						fnpServiceExp.setErrorRollbackBean(UtilCommon.preppareFNPRollback(fnpMessage, paymentAdviceNumber, txnNo, ofcRequest.getUserId()));
						fnpServiceExp.setErrorCode("error.fnp.unexpected");
						throw fnpServiceExp;
					}
					catch(ConnectException connectionExp)
					{
						FNPServiceExpcetion fnpExp=new FNPServiceExpcetion(connectionExp);
						fnpExp.setErrorRollbackBean(UtilCommon.preppareFNPRollback(fnpMessage, paymentAdviceNumber, txnNo, ofcRequest.getUserId()));
						fnpExp.setErrorCode("error.fnp.communication");
						throw fnpExp;
					}
				}
			}
		}
	}
	
	public boolean manipulateRequest(OFCRequest ofcRequest,String userName,boolean isOfficer,RequestAction action)throws CommunicationException,WorkFlowException,InvalidInputException
	{
		Map<String, String> workflowMap = new HashMap<String, String>();
		OFCRequestConvertor ofcRequestConvertor = new OFCRequestConvertor();
		workflowMap = ofcRequestConvertor.convertTo(ofcRequest, workflowMap);
		ofcRequest.setService(Service.ofcService);
		boolean actionStatus = workflowService.doAction(ofcRequest.getProcessId(),userName,action.getActionName(),workflowMap,
				isOfficer);
		
		if(actionStatus)
		{
			updateOFCRequest(ofcRequest);
		}
				
		return actionStatus;
	}
	
	public boolean sendK2Mail(String processId, String username, String actionType, OFCRequest ofcRequest, boolean isOfficer) throws WorkFlowException{
		Map<String, String> workflowMap = new HashMap<String, String>();
		OFCRequestConvertor ofcRequestConvertor = new OFCRequestConvertor();
		workflowMap = ofcRequestConvertor.convertTo(ofcRequest, workflowMap);
		return workflowService.doAction(processId, username, actionType,workflowMap, isOfficer);
	}
	
	private void trackRequestChange(OFCRequest ofcRequest, boolean update) {
		AppMetaDataVO metadata = new AppMetaDataVO();
		metadata.setApplicationDate(UtilCommon.getCurrentDateAsDDMMYYYY());
		metadata.setStrApplicationId(ofcRequest.getId()+"");
		metadata.setApplicationLink(UtilCommon.getContextPath()+"/common/viewDetails.htm?requestId="+ofcRequest.getId());
		if(RequestType.New.equals(ofcRequest.getType())){
			metadata.setProcessCode(WFProcessType.EHS_OPERATION_FITNESS_CERTIFICATE_NEW);
		} else if(RequestType.Amendment.equals(ofcRequest.getType())){
			metadata.setProcessCode(WFProcessType.EHS_OPERATION_FITNESS_CERTIFICATE_AMENDMENT);
		} else {
			metadata.setProcessCode(WFProcessType.EHS_OPERATION_FITNESS_CERTIFICATE_RENEWAL);
		}
		String requestIdFormatted = "";
		requestIdFormatted = ofcRequest.getService().getName().trim() + "_" + ofcRequest.getType().toString().trim() + "_" + UtilCommon.getStringFromDate(ofcRequest.getCreationDate()).trim()+ "_" + ofcRequest.getId();
		metadata.setStrClientRefNo(requestIdFormatted);
		metadata.setStrStatusDescEn(UtilCommon.getResourceMessage(ofcRequest.getStatus().getStatusName(), null, Locale.ENGLISH));
		metadata.setStrStatusDescAr(UtilCommon.getResourceMessage(ofcRequest.getStatus().getStatusName(), null, new Locale("ar")));
		metadata.setStrWFIncidentId(ofcRequest.getProcessId());
		metadata.setStrWFinitatedBy(ofcRequest.getUserId());
		if(update){
			trackAppMetaDataDelegate.updateApplicationRequest(metadata);
		}else {
			trackAppMetaDataDelegate.createApplicationRequest(metadata);
		}
	}

	@Override
	public boolean manipulateIMSRequest(OFCRequest ofcRequest, String userName,
			boolean isOfficer, RequestAction action,String imsStatus, Date processingDate) throws WorkFlowException {
		Map<String, String> workflowMap = new HashMap<String, String>();
		OFCRequestConvertor ofcRequestConvertor = new OFCRequestConvertor();
		workflowMap = ofcRequestConvertor.convertTo(ofcRequest, workflowMap);
		ofcRequest.setService(Service.ofcService);
		boolean actionStatus = workflowService.doAction(ofcRequest.getProcessId(),userName,action.getActionName(),workflowMap,isOfficer);
		if(actionStatus){
			ofcRequest.getFnpList();
			ofcRequest.getComments();
			ofcRequest.setImsStatus(imsStatus);
			ofcRequest.setProcessingDate(processingDate);
			OFCRequestHistory history = new OFCRequestHistory();
			getMapper().map(ofcRequest, history);
			history.setExecuter(UserType.Officer);
			history.setOfficerId("IMS System");
			history.setOfficerComments("");
			history.setLastUpdated(null);
			history.setLastUpdatedBy(null);
			history.setCreatedBy(null);
			history.setWhenCreated(null);
			ofcRequest.getOfcHistory().add(history);
			updateOFCRequest(ofcRequest);
		}
		return actionStatus;
	}
	
	@Override
	public long getActiveRequestSize(String userName){
		return workflowManager.getActiveRequestSize(userName);
	}
}
